' PicBasic Pro program to demonstrate an interrupt-driven
' input buffer for Hserin using On Interrupt.
' Pin definitions compatible with LAB-X1 and PIC16F877


' Defines for LCD and USART

DEFINE HSER_RCSTA 90h
DEFINE HSER_TXSTA 24h
DEFINE HSER_BAUD 9600

DEFINE  LCD_DREG        PORTD
DEFINE  LCD_DBIT        4
DEFINE  LCD_RSREG       PORTE
DEFINE  LCD_RSBIT       0
DEFINE  LCD_EREG        PORTE
DEFINE  LCD_EBIT        1


ADCON1 = 7				' Set PortA and E to digital operation

RCIF	VAR	PIR1.5		' Alias RCIF (USART Receive Interrupt Flag)
OERR	VAR	RCSTA.1		' Alias OERR (USART Overrun Error Flag)
CREN	VAR	RCSTA.4		' Alias CREN (USART Continuous Receive Enable)
LED     VAR	PORTD.0 	' Alias LED to PORTD.0

buffer_size	CON	32		' Sets the size of the ring buffer
buffer	VAR	BYTE[buffer_size]	' Array variable for holding received characters
index_in	VAR	BYTE	' Pointer - next empty location in buffer
index_out	VAR	BYTE	' Pointer - location of oldest character in buffer
bufchar	VAR	BYTE		' Stores the character retrieved from the buffer
i		VAR	BYTE		' loop counter 
col		VAR	BYTE		' Stores location on LCD for text wrapping
errflag	VAR	BYTE		' Holds error flags

' Initialize variables

index_in = 0
index_out = 0
i = 0
col = 1


Low PORTE.2             ' LCD R/W line low (W)
Pause 100               ' Wait for LCD to start

LCDOut $fe,1			' Clear LCD


INTCON = %11000000		' Enable interrupts
ON INTERRUPT GoTo serialin		' Declare interrupt handler routine
PIE1.5 = 1				' Enable interrupt on USART



' Main program starts here - blink an LED at 1Hz

loop:   High LED        ' Turn on LED connected to PORTD.0
        For i = 0 to 250	' Delay for .5 seconds (250*2mS)
	    	Pause 2   	' Use a short pause within a loop
		Next i			' instead of one long pause
        Low LED     	' Turn off LED connected to PORTD.0
        For i = 0 to 250	' Delay for .5 seconds (250*2mS)
	        Pause 2   	' Use a short pause within a loop
		Next i			' instead of one long pause

display:				' dump the buffer to the LCD
		IF errflag Then error	' Handle error if needed
		IF index_in = index_out Then loop	' loop if nothing in buffer

		GoSub getbuf	' Get a character from buffer	
		LCDOut bufchar	' Send the character to LCD
		
		col = col + 1	' Increment LCD location
		IF col > 20 Then	' Check for end of line
			col = 1			' Reset LCD location
			LCDOut $fe,$c0,REP " "\20	' Clear line-2 of LCD
			LCDOut $FE,2	' Tell LCD to return home
		EndIF

GoTo display			' Check for more characters in buffer



' Subroutines

Disable					' Don't check for interrupts in this section

getbuf:					' move the next character in buffer to bufchar
	index_out = (index_out + 1)			' Increment index_out pointer (0 to 63)
	IF index_out > (buffer_size-1) Then index_out = 0	' Reset pointer if outside of buffer
	bufchar = buffer[index_out]			' Read buffer location
Return


error:					' Display error message if buffer has overrun
	IF errflag.1 Then	' Determine the error
		LCDOut $FE,$c0,"Buffer Overrun"	' Display buffer error on line-2
	Else
		LCDOut $FE,$c0,"USART Overrun"	' Display usart error on line-2
	EndIF
	
	LCDOut $fe,2		' Send the LCD cursor back to line-1 home
	For i = 2 to col	' Loop for each column beyond 1
		LCDOut $fe,$14	' Move the cursor right to the right column
	Next i
	
	errflag = 0			' Reset the error flag
	CREN = 0			' Disable continuous receive to clear overrun flag
	CREN = 1			' Enable continuous receive

	GoTo display		' Carry on
	
	
' Interrupt handler

serialin:				' Buffer the character received
	IF OERR Then usart_error			' Check for USART errors
	index_in = (index_in + 1)			' Increment index_in pointer (0 to 63)
	IF index_in > (buffer_size-1) Then index_in = 0	'Reset pointer if outside of buffer
	IF index_in = index_out Then buffer_error	' Check for buffer overrun
	HSerin [buffer[index_in]]			' Read USART and store character to next empty location
	IF RCIF Then serialin				' Check for another character while we're here
	
Resume					' Return to program

buffer_error:
	errflag.1 = 1		' Set the error flag for software
' Move pointer back to avoid corrupting the buffer. MIN insures that it ends up within the buffer.	
	index_in = (index_in - 1) MIN (buffer_size - 1)	
	HSerin [buffer[index_in]]	' Overwrite the last character stored (resets the interrupt flag)
usart_error:
	errflag.0 = 1		' Set the error flag for hardware
	
Resume					' Return to program


End					
